﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using APNS.NET.Extensions;

namespace APNS.NET
{

    public class ApnsConnection : IApnsConnection
    {

        private readonly Stream _apnStream;
        private readonly List<ApnsNotification> _transmittedNotifications;

        // Create connection
        public ApnsConnection(string hostname, int port, X509Certificate apnCertificate)
        {
            if (apnCertificate==null)
                throw new ArgumentNullException("apnCertificate");

            _apnStream = new ApnsStream(hostname, port, apnCertificate);                   
            this._transmittedNotifications = new List<ApnsNotification>();            
        }

        ~ApnsConnection()
        {
            Dispose(false);
        }

        // Transmit a notification using the connection
        public ApnsNotificationResponse TransmitNotification(ApnsNotification notification)
        {
            if(notification==null)
                throw new ArgumentNullException("notification");

            ApnsNotificationResponse response = null;

            // Add to list of transmitted notifications
            int notificationIndex = _transmittedNotifications.Count;
            _transmittedNotifications.Add(notification);

            // Write the notification to the APNS stream
            _apnStream.Write(notification.ToByteArray(notificationIndex));
      
            // Wait for a response
            Thread.Sleep(100);                      

            // See if we get a response
            if (_apnStream.Length > 0)
            {
                // If we get a response then we probably have an error
                byte[] buffer = new byte[6];
                _apnStream.Read(buffer, 0, buffer.Length);
                // Read the response from the stream
                response = ApnsNotificationResponse.FromByteArray(buffer);               
            }


            return response;
        }

        public bool Connected { get { return _apnStream.CanWrite; } }

        // Close the connection
        public void Close()
        {
            _apnStream.Close();
        }

        // Get a notification that was transmitted on the connection
        public ApnsNotification GetTransmittedNotificationWithId(int notificationId)
        {
            ApnsNotification result = null;
            if (notificationId > 0 && notificationId < _transmittedNotifications.Count)
                result = _transmittedNotifications[notificationId];

            return result;
        }

        // Get all notifications that have been transmitted on the connection
        public IEnumerable<ApnsNotification> GetTransmittedNotifications()
        {
            return GetTransmittedNotifications(-1);
        }

        // Get all notifications that have been transmitted on the connection after the specified notification
        public IEnumerable<ApnsNotification> GetTransmittedNotifications(int afterId)
        {
            for (int i = afterId + 1; i < _transmittedNotifications.Count; i++)
            {
                yield return _transmittedNotifications[i];
            }
        }

        // Close the connection
        public void Dispose(bool disposing)
        {
            Close();
            if(disposing)
            {
                _apnStream.Close();
            }
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }
    }
}
